package cleancode.controller.commandexecutor;

import java.util.ArrayList;
import java.util.List;

import cleancode.controller.interpreter.CommandInfo;
import cleancode.controller.interpreter.CommandInterpreter;
import cleancode.controller.interpreter.DefaultCommandInterpreter;
import cleancode.controller.methodinvoker.DefaultOperationMethodInvoker;
import cleancode.controller.methodinvoker.OperationMethodInvoker;
import cleancode.model.NameOrientedModelSet;
import cleancode.model.Operation;
import cleancode.model.PersistentData;
import cleancode.model.Role;
import cleancode.model.UltraUser;
import cleancode.model.User;
import cleancode.util.StringUtils;

/**
 * Executor user command
 */
public class CommandExecutor implements CommandHandler, PersistentDataOperator
{
    private CommandInterpreter commandInterpreter = new DefaultCommandInterpreter();

    private OperationMethodInvoker invoker = new DefaultOperationMethodInvoker( this );

    private PersistentData persistentData;

    private User currentUser;

    public CommandExecutor( PersistentData data )
    {
        persistentData = data;
    }

    /**
     * Return help menu
     */
    public String help()
    {
        return commandInterpreter.getRule();
    }

    public String handleCommand( String command )
    {
        if( command == null || command.isEmpty() )
        {
            return "Command cannot be empty!";
        }
        if( currentUser == null )
        {
            return login( command ) ? command + " successfully login!" : "Please login with valid user name!";
        }
        CommandInfo commandInfo = commandInterpreter.interprete( command );
        if( commandInfo.getErrorMessage() != null )
        {
            return commandInfo.getErrorMessage();
        }
        Operation operation = commandInfo.getOperation();
        if( !isValidOperation( operation ) )
        {
            return "Unknown operation!";
        }
        if( !currentUser.checkPermission( operation ) )
        {
            return "Do not have permission!";
        }
        if( operation.isBasicOperation() )
        {
            return invoker.invoke( commandInfo );
        }
        return command + " executed!";
    }

    private boolean isValidOperation( Operation operation )
    {
        return operation.isBasicOperation() || persistentData.getOperations().contains( operation );
    }

    private boolean login( String userName )
    {
        User user =
            UltraUser.ULTRA_USER_NAME.equals( userName ) ? new UltraUser() : persistentData.getUsers().get( userName );
        if( user != null )
        {
            currentUser = user;
        }
        return user != null;
    }

    public String switchUser( String userName )
    {
        if( login( userName ) )
        {
            return "Switch to " + userName + "!";
        }
        else
        {
            return "Invalid userName!";
        }
    }

    public void addUser( String... names )
    {
        for( String name : names )
        {
            if( UltraUser.ULTRA_USER_NAME.equals( name ) )
            {
                continue;
            }
            if( persistentData.getUsers().get( name ) == null )
            {
                persistentData.getUsers().add( new User( name ) );
            }
        }
    }

    public void addRole( String... names )
    {
        for( String name : names )
        {
            if( persistentData.getRoles().get( name ) == null )
            {
                persistentData.getRoles().add( new Role( name ) );
            }
        }
    }

    public void addOperation( String... names )
    {
        for( String name : names )
        {
            if( persistentData.getOperations().get( name ) == null && !Operation.getBasicOperations().contains( name ) )
            {
                persistentData.getOperations().add( new Operation( name ) );
            }
        }
    }

    public void addRoleTo( String[] roleNames, String[] userNames )
    {
        for( String userName : userNames )
        {
            if( UltraUser.ULTRA_USER_NAME.equals( userName ) )
            {
                continue;
            }
            for( String roleName : roleNames )
            {
                addRole( roleName );
                addUser( userName );
                persistentData.getUsers().get( userName ).getRoles().add( persistentData.getRoles().get( roleName ) );
            }
        }
    }

    public void addOperationTo( String[] operationNames, String[] roleNames )
    {
        for( String roleName : roleNames )
        {
            for( String operationName : operationNames )
            {
                addOperation( operationName );
                addRole( roleName );
                persistentData.getRoles().get( roleName ).getSupportedOperations().add(
                    persistentData.getOperations().get( operationName ) );
            }
        }
    }

    public void removeUser( String... names )
    {
        for( String name : names )
        {
            persistentData.getUsers().remove( name );
        }
    }

    public void removeRole( String... names )
    {
        for( String name : names )
        {
            Role role = persistentData.getRoles().get( name );
            for( User user : persistentData.getUsers() )
            {
                user.getRoles().remove( role );
            }
            persistentData.getRoles().remove( role );
        }
    }

    public void removeOperation( String... names )
    {
        for( String name : names )
        {
            Operation operation = persistentData.getOperations().get( name );
            for( Role role : persistentData.getRoles() )
            {
                role.getSupportedOperations().remove( operation );
            }
            persistentData.getOperations().remove( operation );
        }
    }

    public void removeRoleFrom( String[] roleNames, String[] userNames )
    {
        for( String userName : userNames )
        {
            for( String roleName : roleNames )
            {
                User user = persistentData.getUsers().get( userName );
                if( user != null )
                {
                    user.getRoles().remove( persistentData.getRoles().get( roleName ) );
                }
            }
        }
    }

    public void removeOperationFrom( String[] operationNames, String[] roleNames )
    {
        for( String roleName : roleNames )
        {
            for( String operationName : operationNames )
            {
                Role role = persistentData.getRoles().get( roleName );
                if( role != null )
                {
                    role.getSupportedOperations().remove( persistentData.getOperations().get( operationName ) );
                }
            }
        }
    }

    public String listUser()
    {
        List<String> userList = new ArrayList<String>();
        for( User user : persistentData.getUsers() )
        {
            userList.add( user.getName() + "\n" + StringUtils.combineModelNamesWithComma( user.getRoles() ) + "\n" +
                StringUtils.combineModelNamesWithComma( user.getSupportedOperations() ) );
        }
        return StringUtils.combineCollectionsWithRow( userList );
    }

    public String listRole()
    {
        List<String> roleList = new ArrayList<String>();
        for( Role role : persistentData.getRoles() )
        {
            roleList.add( role.getName() + "\n" +
                StringUtils.combineModelNamesWithComma( role.getSupportedOperations() ) );
        }
        return StringUtils.combineCollectionsWithRow( roleList );
    }

    public String listOperation()
    {
        NameOrientedModelSet<Operation> operations = Operation.getBasicOperations();
        operations.addAll( persistentData.getOperations() );
        return StringUtils.combineModelNamesWithComma( operations );
    }
}
